<xi:include href="xml/windows.xml" />
<xi:include href="xml/gdkframeclock.xml" />
<xi:include href="xml/gdkframetimings.xml" />
+ <xi:include href="xml/gdkglcontext.xml" />
<xi:include href="xml/events.xml" />
<xi:include href="xml/event_structs.xml" />
<xi:include href="xml/keys.xml" />
gdk_window_beep
gdk_window_get_scale_factor
gdk_window_set_opaque_region
+gdk_window_create_gl_context
gdk_window_mark_paint_from_clip
<SUBSECTION>
gdk_cairo_region
gdk_cairo_region_create_from_surface
gdk_cairo_surface_create_from_pixbuf
+gdk_cairo_draw_from_gl
</SECTION>
<SECTION>
gdk_x11_display_error_trap_pop_ignored
gdk_x11_display_set_cursor_theme
gdk_x11_display_set_window_scale
+gdk_x11_display_get_glx_version
gdk_x11_register_standard_event_type
gdk_x11_screen_get_screen_number
gdk_x11_screen_get_xscreen
GDK_IS_X11_KEYMAP
GDK_IS_X11_KEYMAP_CLASS
GDK_X11_KEYMAP_GET_CLASS
+GDK_TYPE_X11_GL_CONTEXT
+GDK_X11_GL_CONTEXT
+GDK_X11_GL_CONTEXT_CLASS
+GDK_IS_X11_GL_CONTEXT
+GDK_IS_X11_GL_CONTEXT_CLASS
GDK_TYPE_X11_SCREEN
GDK_X11_SCREEN
GDK_X11_SCREEN_CLASS
<SUBSECTION Private>
gdk_frame_get_type
</SECTION>
+
+<SECTION>
+<FILE>gdkglcontext</FILE>
+GdkGLContext
+gdk_gl_context_get_visual
+gdk_gl_context_get_window
+gdk_gl_context_make_current
+gdk_gl_context_clear_current
+gdk_gl_context_get_current
+GDK_GL_ERROR
+GDK_TYPE_GL_ERROR
+GDK_TYPE_GL_PROFILE
+
+<SUBSECTION Standard>
+GDK_GL_CONTEXT
+GDK_IS_GL_CONTEXT
+GDK_TYPE_GL_CONTEXT
+GDK_GL_CONTEXT_CLASS
+GDK_GL_CONTEXT_GET_CLASS
+GDK_IS_GL_CONTEXT_CLASS
+gdk_gl_context_get_type
+gdk_gl_error_quark
+</SECTION>
gdk_display_manager_get_type
gdk_drag_context_get_type
gdk_frame_clock_get_type
+gdk_gl_context_get_type
gdk_keymap_get_type
gdk_screen_get_type
gdk_visual_get_type
gdkdnd.h \
gdkevents.h \
gdkframetimings.h \
+ gdkglcontext.h \
gdkkeys.h \
gdkkeysyms.h \
gdkkeysyms-compat.h \
gdkdndprivate.h \
gdkframeclockidle.h \
gdkframeclockprivate.h \
+ gdkglcontextprivate.h \
gdkscreenprivate.h \
gdkinternals.h \
gdkintl.h \
gdkdnd.c \
gdkevents.c \
gdkframetimings.c \
+ gdkgl.c \
+ gdkglcontext.c \
gdkglobals.c \
gdkkeys.c \
gdkkeyuni.c \
{"draw", GDK_DEBUG_DRAW},
{"eventloop", GDK_DEBUG_EVENTLOOP},
{"frames", GDK_DEBUG_FRAMES},
- {"settings", GDK_DEBUG_SETTINGS}
+ {"settings", GDK_DEBUG_SETTINGS},
+ {"opengl", GDK_DEBUG_OPENGL},
};
static gboolean
#include <gdk/gdkevents.h>
#include <gdk/gdkframeclock.h>
#include <gdk/gdkframetimings.h>
+#include <gdk/gdkglcontext.h>
#include <gdk/gdkkeys.h>
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkmain.h>
cairo_surface_t * gdk_cairo_surface_create_from_pixbuf (const GdkPixbuf *pixbuf,
int scale,
GdkWindow *for_window);
+GDK_AVAILABLE_IN_3_16
+void gdk_cairo_draw_from_gl (cairo_t *cr,
+ GdkWindow *window,
+ int source,
+ int source_type,
+ int buffer_scale,
+ int x,
+ int y,
+ int width,
+ int height);
+
G_END_DECLS
{
return gdk_error_trap_pop_internal (TRUE);
}
+
+/*< private >
+ * gdk_display_destroy_gl_context:
+ * @display: a #GdkDisplay
+ * @context: a #GdkGLContext
+ *
+ * Destroys the platform-specific parts of the @context.
+ *
+ * The @context instance is still valid, though inert, after
+ * this functionr returns.
+ */
+void
+gdk_display_destroy_gl_context (GdkDisplay *display,
+ GdkGLContext *context)
+{
+ GdkGLContext *current = gdk_display_get_current_gl_context (display);
+
+ if (current == context)
+ g_object_set_data (G_OBJECT (display), "-gdk-gl-current-context", NULL);
+
+ GDK_DISPLAY_GET_CLASS (display)->destroy_gl_context (display, context);
+}
+
+/*< private >
+ * gdk_display_make_gl_context_current:
+ * @display: a #GdkDisplay
+ * @context: (optional): a #GdkGLContext, or %NULL
+ *
+ * Makes the given @context the current GL context, or unsets
+ * the current GL context if @context is %NULL.
+ *
+ * Returns: %TRUE if successful
+ */
+gboolean
+gdk_display_make_gl_context_current (GdkDisplay *display,
+ GdkGLContext *context)
+{
+ GdkGLContext *current = gdk_display_get_current_gl_context (display);
+
+ if (current == context)
+ return TRUE;
+
+ if (context == NULL)
+ g_object_set_data (G_OBJECT (display), "-gdk-gl-current-context", NULL);
+ else
+ g_object_set_data_full (G_OBJECT (display), "-gdk-gl-current-context",
+ g_object_ref (context),
+ (GDestroyNotify) g_object_unref);
+
+ return GDK_DISPLAY_GET_CLASS (display)->make_gl_context_current (display, context);
+}
+
+/*< private >
+ * gdk_display_get_current_gl_context:
+ * @display: a #GdkDisplay
+ *
+ * Retrieves the current #GdkGLContext associated with @display.
+ *
+ * Returns: (transfer none): the current #GdkGLContext or %NULL
+ */
+GdkGLContext *
+gdk_display_get_current_gl_context (GdkDisplay *display)
+{
+ return g_object_get_data (G_OBJECT (display), "-gdk-gl-current-context");
+}
gchar * (*utf8_to_string_target) (GdkDisplay *display,
const gchar *text);
+ gboolean (*make_gl_context_current) (GdkDisplay *display,
+ GdkGLContext *context);
+ void (*destroy_gl_context) (GdkDisplay *display,
+ GdkGLContext *context);
+
/* Signals */
void (*opened) (GdkDisplay *display);
void (*closed) (GdkDisplay *display,
gint attributes_mask);
GdkWindow * _gdk_display_create_window (GdkDisplay *display);
+void gdk_display_destroy_gl_context (GdkDisplay *display,
+ GdkGLContext *context);
+gboolean gdk_display_make_gl_context_current (GdkDisplay *display,
+ GdkGLContext *context);
+GdkGLContext * gdk_display_get_current_gl_context (GdkDisplay *display);
+
G_END_DECLS
#endif /* __GDK_DISPLAY_PRIVATE_H__ */
--- /dev/null
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gdkcairo.h"
+#include "gdkglcontextprivate.h"
+
+#include "gdkinternals.h"
+
+#include <epoxy/gl.h>
+#include <math.h>
+
+static cairo_user_data_key_t direct_key;
+
+void
+gdk_cairo_surface_mark_as_direct (cairo_surface_t *surface,
+ GdkWindow *window)
+{
+ cairo_surface_set_user_data (surface, &direct_key,
+ g_object_ref (window), g_object_unref);
+}
+
+/* x,y,width,height describes a rectangle in the gl render buffer
+ coordinate space, and its top left corner is drawn at the current
+ position according to the cairo translation. */
+/**
+ * gdk_cairo_draw_from_gl
+ * @cr: a cairo context
+ * @window: The window we're rendering for (not necessarily into)
+ * @source: The GL id of the source buffer
+ * @source_type: The type of the @source
+ * @buffer_scale: The scale-factor that the @source buffer is allocated for
+ * @x: The source x position in @source to start copying from in GL coordinates
+ * @y: The source y position in @source to start copying from in GL coordinates
+ * @width: The width of the region to draw
+ * @height: The height of the region to draw
+ *
+ * This is the main way to draw GL content in Gtk+. It takes a render buffer id
+ * (@source_type == #GL_RENDERBUFFER) or a texture id (@source_type == #GL_TEXTURE)
+ * and draws it onto @cr with an OVER operation, respecting the current clip.
+ *
+ * This will work for *all* cairo_t, as long as @window is realized, but the
+ * fallback implementation that reads back the pixels from the buffer may be
+ * used in the general case. In the case of direct drawing to a window with
+ * no special effects applied to @cr it will however use a more efficient
+ * approach.
+ *
+ * For #GL_RENDERBUFFER the code will always fall back to software for buffers
+ * with alpha components, so make sure you use #GL_TEXTURE if using alpha.
+ */
+void
+gdk_cairo_draw_from_gl (cairo_t *cr,
+ GdkWindow *window,
+ int source,
+ int source_type,
+ int buffer_scale,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ GdkGLContext *context;
+ cairo_surface_t *image;
+ cairo_matrix_t matrix;
+ int dx, dy, window_scale;
+ gboolean trivial_transform;
+ cairo_surface_t *group_target;
+ GdkWindow *direct_window, *impl_window;
+ GLuint framebuffer;
+ GLint alpha_size = 0;
+ cairo_region_t *clip_region;
+
+ impl_window = window->impl_window;
+
+ window_scale = gdk_window_get_scale_factor (impl_window);
+
+ context = gdk_window_get_paint_gl_context (window, NULL);
+ if (context == NULL)
+ {
+ g_warning ("gdk_cairo_draw_gl_render_buffer failed - no paint context");
+ return;
+ }
+
+ clip_region = gdk_cairo_region_from_clip (cr);
+
+ if (!gdk_gl_context_make_current (context))
+ g_error ("make current failed");
+
+ glGenFramebuffersEXT (1, &framebuffer);
+ glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, framebuffer);
+
+ if (source_type == GL_RENDERBUFFER)
+ {
+ glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_ALPHA_SIZE, &alpha_size);
+
+ glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, source);
+ glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_RENDERBUFFER_EXT, source);
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
+ }
+ else if (source_type == GL_TEXTURE)
+ {
+ glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_ALPHA_SIZE, &alpha_size);
+
+ glBindTexture (GL_TEXTURE_2D, source);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, source, 0);
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
+ }
+ else
+ {
+ g_warning ("Unsupported gl source type %d\n", source_type);
+ return;
+ }
+
+ group_target = cairo_get_group_target (cr);
+ direct_window = cairo_surface_get_user_data (group_target, &direct_key);
+
+ cairo_get_matrix (cr, &matrix);
+
+ dx = matrix.x0;
+ dy = matrix.y0;
+
+ /* Trivial == integer-only translation */
+ trivial_transform =
+ (double)dx == matrix.x0 && (double)dy == matrix.y0 &&
+ matrix.xx == 1.0 && matrix.xy == 0.0 &&
+ matrix.yx == 0.0 && matrix.yy == 1.0;
+
+ /* For direct paint of non-alpha renderbuffer, we can
+ just do a bitblit */
+ if (source_type == GL_RENDERBUFFER &&
+ alpha_size == 0 &&
+ direct_window != NULL &&
+ direct_window->current_paint.use_gl &&
+ trivial_transform &&
+ clip_region != NULL)
+ {
+ int window_height;
+ int i;
+
+ /* Translate to impl coords */
+ cairo_region_translate (clip_region, dx, dy);
+
+ glEnable (GL_SCISSOR_TEST);
+
+ window_height = gdk_window_get_height (impl_window);
+ glDrawBuffer (GL_BACK);
+
+#define FLIP_Y(_y) (window_height*window_scale - (_y))
+
+ for (i = 0; i < cairo_region_num_rectangles (clip_region); i++)
+ {
+ cairo_rectangle_int_t clip_rect, dest;
+
+ cairo_region_get_rectangle (clip_region, i, &clip_rect);
+ clip_rect.x *= window_scale;
+ clip_rect.y *= window_scale;
+ clip_rect.width *= window_scale;
+ clip_rect.height *= window_scale;
+
+ glScissor (clip_rect.x, FLIP_Y (clip_rect.y + clip_rect.height),
+ clip_rect.width, clip_rect.height);
+
+ dest.x = dx * window_scale;
+ dest.y = dy * window_scale;
+ dest.width = width * window_scale / buffer_scale;
+ dest.height = height * window_scale / buffer_scale;
+
+ if (gdk_rectangle_intersect (&clip_rect, &dest, &dest))
+ {
+ int clipped_src_x = x + (dest.x - dx * window_scale);
+ int clipped_src_y = y + (height - dest.height - (dest.y - dy * window_scale));
+ glBlitFramebufferEXT(clipped_src_x, clipped_src_y,
+ (clipped_src_x + dest.width), (clipped_src_y + dest.height),
+ dest.x, FLIP_Y(dest.y + dest.height),
+ dest.x + dest.width, FLIP_Y(dest.y),
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ if (impl_window->current_paint.flushed_region)
+ {
+ cairo_rectangle_int_t flushed_rect;
+
+ flushed_rect.x = dest.x / window_scale;
+ flushed_rect.y = dest.y / window_scale;
+ flushed_rect.width = (dest.x + dest.width + window_scale - 1) / window_scale - flushed_rect.x;
+ flushed_rect.height = (dest.y + dest.height + window_scale - 1) / window_scale - flushed_rect.y;
+
+ cairo_region_union_rectangle (impl_window->current_paint.flushed_region,
+ &flushed_rect);
+ cairo_region_subtract_rectangle (impl_window->current_paint.need_blend_region,
+ &flushed_rect);
+ }
+ }
+ }
+
+ glDisable (GL_SCISSOR_TEST);
+
+#undef FLIP_Y
+
+ }
+ /* For direct paint of alpha or non-alpha textures we can use texturing */
+ else if (source_type == GL_TEXTURE &&
+ direct_window != NULL &&
+ direct_window->current_paint.use_gl &&
+ trivial_transform &&
+ clip_region != NULL)
+ {
+ int window_height;
+ GLint texture_width;
+ GLint texture_height;
+ int i;
+
+ /* Translate to impl coords */
+ cairo_region_translate (clip_region, dx, dy);
+
+ glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &texture_width);
+ glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &texture_height);
+
+ if (alpha_size != 0)
+ {
+ cairo_region_t *opaque_region, *blend_region;
+
+ opaque_region = cairo_region_copy (clip_region);
+ cairo_region_subtract (opaque_region, impl_window->current_paint.flushed_region);
+ cairo_region_subtract (opaque_region, impl_window->current_paint.need_blend_region);
+
+ if (!cairo_region_is_empty (opaque_region))
+ gdk_gl_texture_from_surface (impl_window->current_paint.surface,
+ opaque_region);
+
+ blend_region = cairo_region_copy (clip_region);
+ cairo_region_intersect (blend_region, impl_window->current_paint.need_blend_region);
+
+ glEnable (GL_BLEND);
+ if (!cairo_region_is_empty (blend_region))
+ gdk_gl_texture_from_surface (impl_window->current_paint.surface,
+ blend_region);
+
+ cairo_region_destroy (opaque_region);
+ cairo_region_destroy (blend_region);
+ }
+ glEnable (GL_SCISSOR_TEST);
+ glEnable (GL_TEXTURE_2D);
+
+ window_height = gdk_window_get_height (impl_window);
+
+#define FLIP_Y(_y) (window_height*window_scale - (_y))
+
+ for (i = 0; i < cairo_region_num_rectangles (clip_region); i++)
+ {
+ cairo_rectangle_int_t clip_rect, dest;
+
+ cairo_region_get_rectangle (clip_region, i, &clip_rect);
+
+ clip_rect.x *= window_scale;
+ clip_rect.y *= window_scale;
+ clip_rect.width *= window_scale;
+ clip_rect.height *= window_scale;
+
+ glScissor (clip_rect.x, FLIP_Y (clip_rect.y + clip_rect.height),
+ clip_rect.width, clip_rect.height);
+
+ dest.x = dx * window_scale;
+ dest.y = dy * window_scale;
+ dest.width = width * window_scale / buffer_scale;
+ dest.height = height * window_scale / buffer_scale;
+
+ if (gdk_rectangle_intersect (&clip_rect, &dest, &dest))
+ {
+ int clipped_src_x = x + (dest.x - dx * window_scale);
+ int clipped_src_y = y + (height - dest.height - (dest.y - dy * window_scale));
+
+ glBegin (GL_QUADS);
+ glTexCoord2f (clipped_src_x / (float)texture_width, clipped_src_y / (float)texture_height);
+ glVertex2f (dest.x, FLIP_Y(dest.y + dest.height));
+
+ glTexCoord2f ((clipped_src_x + dest.width) / (float)texture_width, clipped_src_y / (float)texture_height);
+ glVertex2f (dest.x + dest.width, FLIP_Y(dest.y + dest.height));
+
+ glTexCoord2f ((clipped_src_x + dest.width) / (float)texture_width, (clipped_src_y + dest.height) / (float)texture_height);
+ glVertex2f (dest.x + dest.width, FLIP_Y(dest.y));
+
+ glTexCoord2f (clipped_src_x / (float)texture_width, (clipped_src_y + dest.height) / (float)texture_height);
+ glVertex2f (dest.x, FLIP_Y(dest.y));
+ glEnd();
+
+ if (impl_window->current_paint.flushed_region)
+ {
+ cairo_rectangle_int_t flushed_rect;
+
+ flushed_rect.x = dest.x / window_scale;
+ flushed_rect.y = dest.y / window_scale;
+ flushed_rect.width = (dest.x + dest.width + window_scale - 1) / window_scale - flushed_rect.x;
+ flushed_rect.height = (dest.y + dest.height + window_scale - 1) / window_scale - flushed_rect.y;
+
+ cairo_region_union_rectangle (impl_window->current_paint.flushed_region,
+ &flushed_rect);
+ cairo_region_subtract_rectangle (impl_window->current_paint.need_blend_region,
+ &flushed_rect);
+ }
+ }
+ }
+
+ if (alpha_size != 0)
+ glDisable (GL_BLEND);
+
+ glDisable (GL_TEXTURE_2D);
+ glDisable (GL_SCISSOR_TEST);
+
+#undef FLIP_Y
+
+ }
+ else
+ {
+ /* Software fallback */
+
+ /* TODO: avoid reading back non-required data due to dest clip */
+ image = cairo_surface_create_similar_image (cairo_get_target (cr),
+ (alpha_size == 0) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32,
+ width, height);
+
+#ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE
+ cairo_surface_set_device_scale (image, buffer_scale, buffer_scale);
+#endif
+
+ glPixelStorei (GL_PACK_ALIGNMENT, 4);
+ glPixelStorei (GL_PACK_ROW_LENGTH, cairo_image_surface_get_stride (image) / 4);
+
+ glReadPixels (x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
+ cairo_image_surface_get_data (image));
+
+ glPixelStorei (GL_PACK_ROW_LENGTH, 0);
+
+ cairo_surface_mark_dirty (image);
+
+ /* Invert due to opengl having different origin */
+ cairo_scale (cr, 1, -1);
+ cairo_translate (cr, 0, -height / buffer_scale);
+
+ cairo_set_source_surface (cr, image, 0, 0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_paint (cr);
+
+ cairo_surface_destroy (image);
+ }
+
+ glDrawBuffer (GL_BACK);
+ glReadBuffer(GL_BACK);
+
+ glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
+ glDeleteFramebuffersEXT (1, &framebuffer);
+
+ if (clip_region)
+ cairo_region_destroy (clip_region);
+}
+
+void
+gdk_gl_texture_from_surface (cairo_surface_t *surface,
+ cairo_region_t *region)
+{
+ GdkGLContext *current;
+ cairo_surface_t *image;
+ double device_x_offset, device_y_offset;
+ cairo_rectangle_int_t rect, e;
+ int n_rects, i;
+ GdkWindow *window;
+ int window_height;
+ unsigned int texture_id;
+ int window_scale;
+ double sx, sy;
+
+ current = gdk_gl_context_get_current ();
+ if (current &&
+ GDK_GL_CONTEXT_GET_CLASS (current)->texture_from_surface &&
+ GDK_GL_CONTEXT_GET_CLASS (current)->texture_from_surface (current, surface, region))
+ return;
+
+ /* Software fallback */
+
+ window = gdk_gl_context_get_window (gdk_gl_context_get_current ());
+ window_scale = gdk_window_get_scale_factor (window);
+ window_height = gdk_window_get_height (window);
+
+ sx = sy = 1;
+#ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE
+ cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy);
+#endif
+
+ cairo_surface_get_device_offset (surface,
+ &device_x_offset, &device_y_offset);
+
+ glGenTextures (1, &texture_id);
+ glBindTexture (GL_TEXTURE_RECTANGLE_ARB, texture_id);
+ glEnable (GL_TEXTURE_RECTANGLE_ARB);
+
+ n_rects = cairo_region_num_rectangles (region);
+ for (i = 0; i < n_rects; i++)
+ {
+ cairo_region_get_rectangle (region, i, &rect);
+
+ glScissor (rect.x * window_scale, (window_height - rect.y - rect.height) * window_scale,
+ rect.width * window_scale, rect.height * window_scale);
+
+ e = rect;
+ e.x *= sx;
+ e.y *= sy;
+ e.x += (int)device_x_offset;
+ e.y += (int)device_y_offset;
+ e.width *= sx;
+ e.height *= sy;
+ image = cairo_surface_map_to_image (surface, &e);
+
+ glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, cairo_image_surface_get_stride (image)/4);
+ glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 4, e.width, e.height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
+ cairo_image_surface_get_data (image));
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
+
+ cairo_surface_unmap_image (surface, image);
+
+#define FLIP_Y(_y) (window_height - (_y))
+
+ glBegin (GL_QUADS);
+ glTexCoord2f (0.0f * sx, rect.height * sy);
+ glVertex2f (rect.x * window_scale, FLIP_Y(rect.y + rect.height) * window_scale);
+
+ glTexCoord2f (rect.width * sx, rect.height * sy);
+ glVertex2f ((rect.x + rect.width) * window_scale, FLIP_Y(rect.y + rect.height) * window_scale);
+
+ glTexCoord2f (rect.width * sx, 0.0f * sy);
+ glVertex2f ((rect.x + rect.width) * window_scale, FLIP_Y(rect.y) * window_scale);
+
+ glTexCoord2f (0.0f * sx, 0.0f * sy);
+ glVertex2f (rect.x * window_scale, FLIP_Y(rect.y) * window_scale);
+ glEnd();
+ }
+
+ glDisable (GL_TEXTURE_RECTANGLE_ARB);
+ glDeleteTextures (1, &texture_id);
+}
--- /dev/null
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkglcontext.c: GL context abstraction
+ *
+ * Copyright © 2014 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gdkglcontext
+ * @Title: GdkGLContext
+ * @Short_description: OpenGL context
+ *
+ * #GdkGLContext is an object representing the platform-specific
+ * OpenGL drawing context.
+ *
+ * #GdkGLContexts are created for a #GdkWindow using gdk_window_create_gl_context(), and
+ * the context will be tied to the native window backing that window, matching the
+ * GdkVisual of the window.
+ *
+ * A #GdkGLContexts normal framebuffer draws directly on to the back buffer of the native
+ * window backing the #GdkWindow, so its not allowed to draw directly to that, as the
+ * gdk repaint system is in full control of that. Instead you can create render buffers
+ * or textures and use gdk_cairo_draw_from_gl() in the draw function of your widget
+ * to draw them. Then Gdk will handle the integration of your rendering with that of
+ * other widgets.
+ *
+ * Support for #GdkGLContext is platform specific, context creation can fail, returning
+ * a %NULL context.
+ *
+ * A #GdkGLContext has to be made "current" in order to start using
+ * it, otherwise any OpenGL call will be ignored.
+ *
+ * ## Creating a new OpenGL context ##
+ *
+ * In order to create a new #GdkGLContext instance you need a
+ * #GdkWindow, which you typically get during the realize call of a widget.
+ *
+ * ## Using a GdkGLContext ##
+ *
+ * You will need to make the #GdkGLContext the current context
+ * before issuing OpenGL calls; the system sends OpenGL commands to
+ * whichever context is current. It is possible to have multiple
+ * contexts, so you always need to ensure that the one which you
+ * want to draw with is the current one before issuing commands:
+ *
+ * |[<!-- language="C" -->
+ * gdk_gl_context_make_current (context);
+ * ]|
+ *
+ * You can now perform your drawing using OpenGL commands.
+ *
+ * You can check which #GdkGLContext is the current one by using
+ * gdk_gl_context_get_current(); you can also unset any #GdkGLContext
+ * that is currently set by calling gdk_gl_context_clear_current().
+ */
+
+#include "config.h"
+
+#include "gdkglcontextprivate.h"
+#include "gdkdisplayprivate.h"
+#include "gdkvisual.h"
+#include "gdkinternals.h"
+
+#include "gdkintl.h"
+
+typedef struct {
+ GdkWindow *window;
+ GdkVisual *visual;
+} GdkGLContextPrivate;
+
+enum {
+ PROP_0,
+
+ PROP_WINDOW,
+ PROP_VISUAL,
+
+ LAST_PROP
+};
+
+static GParamSpec *obj_pspecs[LAST_PROP] = { NULL, };
+
+G_DEFINE_QUARK (gdk-gl-error-quark, gdk_gl_error)
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkGLContext, gdk_gl_context, G_TYPE_OBJECT)
+
+static void
+gdk_gl_context_dispose (GObject *gobject)
+{
+ GdkGLContext *context = GDK_GL_CONTEXT (gobject);
+ GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
+
+ gdk_display_destroy_gl_context (gdk_window_get_display (priv->window), context);
+
+ g_clear_object (&priv->window);
+ g_clear_object (&priv->visual);
+
+ G_OBJECT_CLASS (gdk_gl_context_parent_class)->dispose (gobject);
+}
+
+static void
+gdk_gl_context_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private ((GdkGLContext *) gobject);
+
+ switch (prop_id)
+ {
+ case PROP_WINDOW:
+ {
+ GdkWindow *window = g_value_get_object (value);
+
+ if (window)
+ g_object_ref (window);
+
+ if (priv->window)
+ g_object_unref (priv->window);
+
+ priv->window = window;
+ }
+ break;
+
+ case PROP_VISUAL:
+ {
+ GdkVisual *visual = g_value_get_object (value);
+
+ if (visual != NULL)
+ priv->visual = g_object_ref (visual);
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+gdk_gl_context_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private ((GdkGLContext *) gobject);
+
+ switch (prop_id)
+ {
+ case PROP_WINDOW:
+ g_value_set_object (value, priv->window);
+ break;
+
+ case PROP_VISUAL:
+ g_value_set_object (value, priv->visual);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+gdk_gl_context_class_init (GdkGLContextClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ /**
+ * GdkGLContext:window:
+ *
+ * The #GdkWindow the gl context is bound to.
+ *
+ * Since: 3.16
+ */
+ obj_pspecs[PROP_WINDOW] =
+ g_param_spec_object ("window",
+ P_("Window"),
+ P_("The GDK window bound to the GL context"),
+ GDK_TYPE_WINDOW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GdkGLContext:visual:
+ *
+ * The #GdkVisual matching the pixel format used by the context.
+ *
+ * Since: 3.16
+ */
+ obj_pspecs[PROP_VISUAL] =
+ g_param_spec_object ("visual",
+ P_("Visual"),
+ P_("The GDK visual used by the GL context"),
+ GDK_TYPE_VISUAL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ gobject_class->set_property = gdk_gl_context_set_property;
+ gobject_class->get_property = gdk_gl_context_get_property;
+ gobject_class->dispose = gdk_gl_context_dispose;
+
+ g_object_class_install_properties (gobject_class, LAST_PROP, obj_pspecs);
+}
+
+static void
+gdk_gl_context_init (GdkGLContext *self)
+{
+}
+
+/**
+ * gdk_gl_context_get_visual:
+ * @context: a #GdkGLContext
+ *
+ * Retrieves the #GdkVisual associated with the @context.
+ *
+ * Returns: (transfer none): the #GdkVisual
+ *
+ * Since: 3.16
+ */
+GdkVisual *
+gdk_gl_context_get_visual (GdkGLContext *context)
+{
+ GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
+
+ g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+
+ return priv->visual;
+}
+
+/*< private >
+ * gdk_gl_context_flush_buffer:
+ * @context: a #GdkGLContext
+ * @painted: The area that has been redrawn this frame
+ * @damage: The area that we know is actually different from the last frame
+ *
+ * Copies the back buffer to the front buffer.
+ *
+ * This function may call `glFlush()` implicitly before returning; it
+ * is not recommended to call `glFlush()` explicitly before calling
+ * this function.
+ *
+ * Since: 3.16
+ */
+void
+gdk_gl_context_flush_buffer (GdkGLContext *context,
+ cairo_region_t *painted,
+ cairo_region_t *damage)
+{
+ g_return_if_fail (GDK_IS_GL_CONTEXT (context));
+
+ GDK_GL_CONTEXT_GET_CLASS (context)->flush_buffer (context, painted, damage);
+}
+
+/**
+ * gdk_gl_context_make_current:
+ * @context: a #GdkGLContext
+ *
+ * Makes the @context the current one.
+ *
+ * Returns: %TRUE if the context is current
+ *
+ * Since: 3.16
+ */
+gboolean
+gdk_gl_context_make_current (GdkGLContext *context)
+{
+ GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
+
+ g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
+
+ return gdk_display_make_gl_context_current (gdk_window_get_display (priv->window), context);
+}
+
+/**
+ * gdk_gl_context_get_window:
+ * @context: a #GdkGLContext
+ *
+ * Retrieves the #GdkWindow used by the @context.
+ *
+ * Returns: (transfer none): a #GdkWindow or %NULL
+ *
+ * Since: 3.16
+ */
+GdkWindow *
+gdk_gl_context_get_window (GdkGLContext *context)
+{
+ GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
+
+ g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+
+ return priv->window;
+}
+
+/**
+ * gdk_gl_context_clear_current:
+ *
+ * Clears the current #GdkGLContext.
+ *
+ * Any OpenGL call after this function returns will be ignored
+ * until gdk_gl_context_make_current() is called.
+ *
+ * Since: 3.16
+ */
+void
+gdk_gl_context_clear_current (void)
+{
+ GdkDisplay *display = gdk_display_get_default ();
+
+ gdk_display_make_gl_context_current (display, NULL);
+}
+
+/**
+ * gdk_gl_context_get_current:
+ *
+ * Retrieves the current #GdkGLContext.
+ *
+ * Returns: (transfer none): the current #GdkGLContext, or %NULL
+ *
+ * Since: 3.16
+ */
+GdkGLContext *
+gdk_gl_context_get_current (void)
+{
+ GdkDisplay *display = gdk_display_get_default ();
+
+ return gdk_display_get_current_gl_context (display);
+}
--- /dev/null
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkglcontext.h: GL context abstraction
+ *
+ * Copyright © 2014 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_GL_CONTEXT_H__
+#define __GDK_GL_CONTEXT_H__
+
+#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+#include <gdk/gdkversionmacros.h>
+#include <gdk/gdktypes.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_GL_CONTEXT (gdk_gl_context_get_type ())
+#define GDK_GL_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_GL_CONTEXT, GdkGLContext))
+#define GDK_IS_GL_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_GL_CONTEXT))
+
+#define GDK_GL_ERROR (gdk_gl_error_quark ())
+
+GDK_AVAILABLE_IN_3_16
+GQuark gdk_gl_error_quark (void);
+
+GDK_AVAILABLE_IN_3_16
+GType gdk_gl_context_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_16
+GdkVisual * gdk_gl_context_get_visual (GdkGLContext *context);
+GDK_AVAILABLE_IN_3_16
+GdkWindow * gdk_gl_context_get_window (GdkGLContext *context);
+
+GDK_AVAILABLE_IN_3_16
+gboolean gdk_gl_context_make_current (GdkGLContext *context);
+GDK_AVAILABLE_IN_3_16
+GdkGLContext * gdk_gl_context_get_current (void);
+GDK_AVAILABLE_IN_3_16
+void gdk_gl_context_clear_current (void);
+
+G_END_DECLS
+
+#endif /* __GDK_GL_CONTEXT_H__ */
--- /dev/null
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkglcontextprivate.h: GL context abstraction
+ *
+ * Copyright © 2014 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_GL_CONTEXT_PRIVATE_H__
+#define __GDK_GL_CONTEXT_PRIVATE_H__
+
+#include "gdkglcontext.h"
+
+G_BEGIN_DECLS
+
+#define GDK_GL_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_GL_CONTEXT, GdkGLContextClass))
+#define GDK_IS_GL_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_GL_CONTEXT))
+#define GDK_GL_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_GL_CONTEXT, GdkGLContextClass))
+
+typedef struct _GdkGLContextClass GdkGLContextClass;
+
+struct _GdkGLContext
+{
+ GObject parent_instance;
+};
+
+struct _GdkGLContextClass
+{
+ GObjectClass parent_class;
+
+ void (* update) (GdkGLContext *context);
+ void (* flush_buffer) (GdkGLContext *context,
+ cairo_region_t *painted,
+ cairo_region_t *damage);
+ gboolean (* texture_from_surface) (GdkGLContext *context,
+ cairo_surface_t *surface,
+ cairo_region_t *region);
+};
+
+void gdk_gl_context_flush_buffer (GdkGLContext *context,
+ cairo_region_t *painted,
+ cairo_region_t *damage);
+
+G_END_DECLS
+
+#endif /* __GDK_GL_CONTEXT_PRIVATE_H__ */
GDK_DEBUG_DRAW = 1 << 9,
GDK_DEBUG_EVENTLOOP = 1 << 10,
GDK_DEBUG_FRAMES = 1 << 11,
- GDK_DEBUG_SETTINGS = 1 << 12
+ GDK_DEBUG_SETTINGS = 1 << 12,
+ GDK_DEBUG_OPENGL = 1 << 13
} GdkDebugFlag;
typedef enum {
struct {
cairo_region_t *region;
cairo_surface_t *surface;
+
+ /* Areas of region that have been copied to the back buffer already */
+ cairo_region_t *flushed_region;
+ /* Areas of region that have been copied to the back buffer but
+ needs furter blending of surface data. These two regions are
+ always non-intersecting. */
+ cairo_region_t *need_blend_region;
+
gboolean surface_needs_composite;
+ gboolean use_gl;
} current_paint;
+ GdkGLContext *gl_paint_context;
cairo_region_t *update_area;
guint update_freeze_count;
gboolean _gdk_cairo_surface_extents (cairo_surface_t *surface,
GdkRectangle *extents);
+void gdk_gl_texture_from_surface (cairo_surface_t *surface,
+ cairo_region_t *region);
+void gdk_cairo_surface_mark_as_direct (cairo_surface_t *surface,
+ GdkWindow *window);
cairo_region_t *gdk_cairo_region_from_clip (cairo_t *cr);
void _gdk_window_clear_update_area (GdkWindow *window);
void _gdk_window_update_size (GdkWindow *window);
gboolean _gdk_window_update_viewable (GdkWindow *window);
+GdkGLContext * gdk_window_get_paint_gl_context (GdkWindow *window,
+ GError **error);
+
void _gdk_window_process_updates_recurse (GdkWindow *window,
cairo_region_t *expose_region);
typedef struct _GdkKeymap GdkKeymap;
typedef struct _GdkAppLaunchContext GdkAppLaunchContext;
+typedef struct _GdkGLContext GdkGLContext;
+
/**
* GdkByteOrder:
* @GDK_LSB_FIRST: The values are stored with the least-significant byte
gint y;
};
+/**
+ * GdkGLProfile:
+ * @GDK_GL_PROFILE_DEFAULT: ...
+ * @GDK_GL_PROFILE_LEGACY: ...
+ * @GDK_GL_PROFILE_3_2_CORE: ...
+ *
+ * ...
+ */
+typedef enum {
+ GDK_GL_PROFILE_DEFAULT,
+ GDK_GL_PROFILE_LEGACY,
+ GDK_GL_PROFILE_3_2_CORE
+} GdkGLProfile;
+
+typedef enum {
+ GDK_GL_ERROR_NOT_AVAILABLE,
+ GDK_GL_ERROR_UNSUPPORTED_FORMAT,
+ GDK_GL_ERROR_UNSUPPORTED_PROFILE
+} GdkGLError;
G_END_DECLS
-
#endif /* __GDK_TYPES_H__ */
#include "gdkmarshalers.h"
#include "gdkframeclockidle.h"
#include "gdkwindowimpl.h"
+#include "gdkglcontextprivate.h"
#include <math.h>
+#include <epoxy/gl.h>
+
/* for the use of round() */
#include "fallback-c89.c"
static void gdk_window_set_frame_clock (GdkWindow *window,
GdkFrameClock *clock);
+static void draw_ugly_color (GdkWindow *window,
+ const cairo_region_t *region,
+ int color);
+
+
static guint signals[LAST_SIGNAL] = { 0 };
static gpointer parent_class = NULL;
cairo_region_destroy (window->current_paint.region);
window->current_paint.region = NULL;
+ cairo_region_destroy (window->current_paint.flushed_region);
+ window->current_paint.flushed_region = NULL;
+
+ cairo_region_destroy (window->current_paint.need_blend_region);
+ window->current_paint.need_blend_region = NULL;
+
window->current_paint.surface_needs_composite = FALSE;
}
return GDK_WINDOW_IMPL_GET_CLASS (window->impl)->ref_cairo_surface (gdk_window_get_impl_window (window));
}
+GdkGLContext *
+gdk_window_get_paint_gl_context (GdkWindow *window, GError **error)
+{
+ if (window->impl_window->gl_paint_context == NULL)
+ window->impl_window->gl_paint_context =
+ GDK_WINDOW_IMPL_GET_CLASS (window->impl)->create_gl_context (window,
+ GDK_GL_PROFILE_DEFAULT,
+ NULL,
+ error);
+
+ return window->impl_window->gl_paint_context;
+}
+
+/**
+ * gdk_window_create_gl_context:
+ * @window: a #GdkWindow
+ * @profile: the GL profile the context should target
+ * @error: return location for an error
+ *
+ * Creates a new #GdkGLContext for the given window, matching the
+ * framebuffer format to the visual of the #GdkWindow.
+ *
+ * If the creation of the #GdkGLContext failed, @error will be set.
+ *
+ * Returns: (transfer full): the newly created #GdkGLContext, or
+ * %NULL on error
+ *
+ * Since: 3.16
+ **/
+GdkGLContext *
+gdk_window_create_gl_context (GdkWindow *window,
+ GdkGLProfile profile,
+ GError **error)
+{
+ GdkGLContext *paint_context;
+
+ g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ paint_context = gdk_window_get_paint_gl_context (window, error);
+ if (paint_context == NULL)
+ return NULL;
+
+ return GDK_WINDOW_IMPL_GET_CLASS (window->impl)->create_gl_context (window,
+ profile,
+ paint_context,
+ error);
+}
+
/**
* gdk_window_begin_paint_rect:
* @window: a #GdkWindow
GdkWindowImplClass *impl_class;
double sx, sy;
gboolean needs_surface;
+ cairo_content_t surface_content;
g_return_if_fail (GDK_IS_WINDOW (window));
needs_surface = impl_class->begin_paint_region (window, region);
window->current_paint.region = cairo_region_copy (region);
-
cairo_region_intersect (window->current_paint.region, window->clip_region);
cairo_region_get_extents (window->current_paint.region, &clip_box);
+ window->current_paint.flushed_region = cairo_region_create ();
+ window->current_paint.need_blend_region = cairo_region_create ();
+
+ surface_content = gdk_window_get_content (window);
+
+ window->current_paint.use_gl = window->impl_window->gl_paint_context != NULL;
+
+ if (window->current_paint.use_gl)
+ {
+ GdkGLContext *context;
+
+ int ww = gdk_window_get_width (window) * gdk_window_get_scale_factor (window);
+ int wh = gdk_window_get_height (window) * gdk_window_get_scale_factor (window);
+
+ context = gdk_window_get_paint_gl_context (window, NULL);
+ if (context == NULL || !gdk_gl_context_make_current (context))
+ {
+ g_warning ("gl rendering failed, context: %p", context);
+ window->current_paint.use_gl = FALSE;
+ }
+ else
+ {
+ /* With gl we always need a surface to combine the gl
+ drawing with the native drawing. */
+ needs_surface = TRUE;
+ /* Also, we need the surface to include alpha */
+ surface_content = CAIRO_CONTENT_COLOR_ALPHA;
+
+ /* Initial setup */
+ glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
+ glDisable (GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glViewport (0, 0, ww, wh);
+
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ glOrtho (0.0f, ww, 0.0f, wh, -1.0f, 1.0f);
+
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+ }
+ }
+
if (needs_surface)
{
window->current_paint.surface = gdk_window_create_similar_surface (window,
- gdk_window_get_content (window),
+ surface_content,
MAX (clip_box.width, 1),
MAX (clip_box.height, 1));
sx = sy = 1;
cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy);
#endif
cairo_surface_set_device_offset (window->current_paint.surface, -clip_box.x*sx, -clip_box.y*sy);
+ gdk_cairo_surface_mark_as_direct (window->current_paint.surface, window);
window->current_paint.surface_needs_composite = TRUE;
}
**/
void
gdk_window_mark_paint_from_clip (GdkWindow *window,
- cairo_t *cr)
+ cairo_t *cr)
{
+ cairo_region_t *clip_region;
+ GdkWindow *impl_window = window->impl_window;
+
+ if (impl_window->current_paint.surface == NULL ||
+ cairo_get_target (cr) != impl_window->current_paint.surface)
+ return;
+
+ if (cairo_region_is_empty (impl_window->current_paint.flushed_region))
+ return;
+
+ /* This here seems a bit weird, but basically, we're taking the current
+ clip and applying also the flushed region, and the result is that the
+ new clip is the intersection of these. This is the area where the newly
+ drawn region overlaps a previosly flushed area, which is an area of the
+ double buffer surface that need to be blended OVER the back buffer rather
+ than SRCed. */
+ cairo_save (cr);
+ /* We set the indentity matrix here so we get and apply regions in native
+ window coordinates. */
+ cairo_identity_matrix (cr);
+ gdk_cairo_region (cr, impl_window->current_paint.flushed_region);
+ cairo_clip (cr);
+
+ clip_region = gdk_cairo_region_from_clip (cr);
+ if (clip_region == NULL)
+ {
+ /* Failed to represent clip as region, mark all as requiring
+ blend */
+ cairo_region_union (impl_window->current_paint.need_blend_region,
+ impl_window->current_paint.flushed_region);
+ cairo_region_destroy (impl_window->current_paint.flushed_region);
+ impl_window->current_paint.flushed_region = cairo_region_create ();
+ }
+ else
+ {
+ cairo_region_subtract (impl_window->current_paint.flushed_region, clip_region);
+ cairo_region_union (impl_window->current_paint.need_blend_region, clip_region);
+ }
+
+ /* Clear the area on the double buffer surface to transparent so we
+ can start drawing from scratch the area "above" the flushed
+ region */
+ cairo_set_source_rgba (cr, 0, 0, 0, 0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+
+ cairo_restore (cr);
}
/**
GdkWindow *composited;
GdkWindowImplClass *impl_class;
GdkRectangle clip_box = { 0, };
- cairo_region_t *full_clip;
cairo_t *cr;
g_return_if_fail (GDK_IS_WINDOW (window));
if (impl_class->end_paint)
impl_class->end_paint (window);
+
if (window->current_paint.surface_needs_composite)
{
cairo_surface_t *surface;
- gboolean skip_alpha_blending;
cairo_region_get_extents (window->current_paint.region, &clip_box);
- full_clip = cairo_region_copy (window->clip_region);
- cairo_region_intersect (full_clip, window->current_paint.region);
- surface = gdk_window_ref_impl_surface (window);
- cr = cairo_create (surface);
- cairo_surface_destroy (surface);
+ if (window->current_paint.use_gl)
+ {
+ cairo_region_t *opaque_region = cairo_region_copy (window->current_paint.region);
+ cairo_region_subtract (opaque_region, window->current_paint.flushed_region);
+ cairo_region_subtract (opaque_region, window->current_paint.need_blend_region);
- cairo_set_source_surface (cr, window->current_paint.surface, 0, 0);
- gdk_cairo_region (cr, full_clip);
- cairo_clip (cr);
+ if (!gdk_gl_context_make_current (window->gl_paint_context))
+ g_error ("make current failed");
- /* We can skip alpha blending for a fast composite case
- * if we have an impl window or we're a fully opaque window. */
- skip_alpha_blending = (gdk_window_has_impl (window) ||
- window->alpha == 255);
+ if (!cairo_region_is_empty (opaque_region))
+ gdk_gl_texture_from_surface (window->current_paint.surface,
+ opaque_region);
+ if (!cairo_region_is_empty (window->current_paint.need_blend_region))
+ {
+ glEnable(GL_BLEND);
+ gdk_gl_texture_from_surface (window->current_paint.surface,
+ window->current_paint.need_blend_region);
+ glDisable(GL_BLEND);
+ }
- if (skip_alpha_blending)
- {
- cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
- cairo_paint (cr);
+ cairo_region_destroy (opaque_region);
+
+ gdk_gl_context_flush_buffer (window->gl_paint_context,
+ window->current_paint.region,
+ window->active_update_area);
+
+ if (epoxy_has_gl_extension ("GL_GREMEDY_frame_terminator"))
+ glFrameTerminatorGREMEDY();
}
else
{
- cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
- cairo_paint_with_alpha (cr, window->alpha / 255.0);
- }
+ surface = gdk_window_ref_impl_surface (window);
+ cr = cairo_create (surface);
+ cairo_surface_destroy (surface);
+
+ cairo_set_source_surface (cr, window->current_paint.surface, 0, 0);
+ gdk_cairo_region (cr, window->current_paint.region);
+ cairo_clip (cr);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
- cairo_destroy (cr);
- cairo_region_destroy (full_clip);
+ cairo_destroy (cr);
+ }
}
gdk_window_free_current_paint (window);
expose_region = cairo_region_copy (window->active_update_area);
+ /* Sometimes we can't just paint only the new area, as the windowing system
+ requires more to be repainted. For instance, with opengl you typically
+ repaint all of each frame each time and then swap the buffer, although
+ there are extensions that allow us to reuse part of an old frame */
+ if (GDK_WINDOW_IMPL_GET_CLASS (window->impl)->invalidate_for_new_frame)
+ GDK_WINDOW_IMPL_GET_CLASS (window->impl)->invalidate_for_new_frame (window, expose_region);
+
/* Clip to part visible in impl window */
cairo_region_intersect (expose_region, window->clip_region);
if (debug_updates)
{
+ cairo_region_t *swap_region = cairo_region_copy (expose_region);
+ cairo_region_subtract (swap_region, window->active_update_area);
+ draw_ugly_color (window, swap_region, 1);
+
/* Make sure we see the red invalid area before redrawing. */
gdk_display_sync (gdk_window_get_display (window));
g_usleep (70000);
static void
draw_ugly_color (GdkWindow *window,
- const cairo_region_t *region)
+ const cairo_region_t *region,
+ int color)
{
cairo_t *cr;
cr = gdk_cairo_create (window);
/* Draw ugly color all over the newly-invalid region */
- cairo_set_source_rgb (cr, 50000/65535., 10000/65535., 10000/65535.);
+ if (color == 0)
+ cairo_set_source_rgb (cr, 50000/65535., 10000/65535., 10000/65535.);
+ else
+ cairo_set_source_rgb (cr, 10000/65535., 50000/65535., 10000/65535.);
gdk_cairo_region (cr, region);
cairo_fill (cr);
invalidate_impl_subwindows (window, region, child_func, user_data, 0, 0);
if (debug_updates)
- draw_ugly_color (window, visible_region);
+ draw_ugly_color (window, visible_region, 0);
while (window != NULL &&
!cairo_region_is_empty (visible_region))
gboolean gdk_window_show_window_menu (GdkWindow *window,
GdkEvent *event);
+GDK_AVAILABLE_IN_3_16
+GdkGLContext * gdk_window_create_gl_context (GdkWindow *window,
+ GdkGLProfile profile,
+ GError **error);
+
+
G_END_DECLS
#endif /* __GDK_WINDOW_H__ */
gint bottom);
gboolean (* show_window_menu) (GdkWindow *window,
GdkEvent *event);
+ GdkGLContext *(*create_gl_context) (GdkWindow *window,
+ GdkGLProfile profile,
+ GdkGLContext *share,
+ GError **error);
+ void (*invalidate_for_new_frame)(GdkWindow *window,
+ cairo_region_t *update_area);
};
/* Interface Functions */
gdkeventtranslator.c \
gdkeventtranslator.h \
gdkgeometry-x11.c \
+ gdkglcontext-x11.c \
+ gdkglcontext-x11.h \
gdkkeys-x11.c \
gdkmain-x11.c \
gdkproperty-x11.c \
gdkx11display.h \
gdkx11displaymanager.h \
gdkx11dnd.h \
+ gdkx11glcontext.h \
gdkx11keys.h \
gdkx11property.h \
gdkx11screen.h \
#include "gdkdisplay-x11.h"
#include "gdkprivate-x11.h"
#include "gdkscreen-x11.h"
+#include "gdkglcontext-x11.h"
#include <glib.h>
#include <glib/gprintf.h>
display_class->text_property_to_utf8_list = _gdk_x11_display_text_property_to_utf8_list;
display_class->utf8_to_string_target = _gdk_x11_display_utf8_to_string_target;
+ display_class->destroy_gl_context = gdk_x11_display_destroy_gl_context;
+ display_class->make_gl_context_current = gdk_x11_display_make_gl_context_current;
+
_gdk_x11_windowing_init ();
}
GSList *error_traps;
gint wm_moveresize_button;
+
+ /* GLX information */
+ gint glx_version;
+ gint glx_error_base;
+ gint glx_event_base;
+
+ guint have_glx : 1;
+
+ /* GLX extensions we check */
+ guint has_glx_swap_interval : 1;
+ guint has_glx_create_context : 1;
+ guint has_glx_texture_from_pixmap : 1;
+ guint has_glx_video_sync : 1;
+ guint has_glx_buffer_age : 1;
+ guint has_glx_sync_control : 1;
};
struct _GdkX11DisplayClass
--- /dev/null
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkglcontext-x11.c: X11 specific OpenGL wrappers
+ *
+ * Copyright © 2014 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gdkglcontext-x11.h"
+#include "gdkdisplay-x11.h"
+#include "gdkscreen-x11.h"
+
+#include "gdkx11display.h"
+#include "gdkx11glcontext.h"
+#include "gdkx11screen.h"
+#include "gdkx11window.h"
+#include "gdkx11visual.h"
+
+#include "gdkinternals.h"
+
+#include "gdkintl.h"
+
+#include <cairo/cairo-xlib.h>
+
+#include <GL/glx.h>
+
+G_DEFINE_TYPE (GdkX11GLContext, gdk_x11_gl_context, GDK_TYPE_GL_CONTEXT)
+
+typedef struct {
+ GLXDrawable drawable;
+
+ GdkDisplay *display;
+ GdkWindow *window;
+
+ guint32 last_frame_counter;
+} DrawableInfo;
+
+static void
+drawable_info_free (gpointer data_)
+{
+ DrawableInfo *data = data_;
+
+ gdk_x11_display_error_trap_push (data->display);
+
+ if (data->drawable)
+ glXDestroyWindow (gdk_x11_display_get_xdisplay (data->display), data->drawable);
+
+ gdk_x11_display_error_trap_pop_ignored (data->display);
+
+ g_slice_free (DrawableInfo, data);
+}
+
+static DrawableInfo *
+get_glx_drawable_info (GdkWindow *window)
+{
+ return g_object_get_data (G_OBJECT (window), "-gdk-x11-window-glx-info");
+}
+
+static void
+set_glx_drawable_info (GdkWindow *window,
+ DrawableInfo *info)
+{
+ g_object_set_data_full (G_OBJECT (window), "-gdk-x11-window-glx-info",
+ info,
+ drawable_info_free);
+}
+
+static void
+gdk_x11_gl_context_update (GdkGLContext *context)
+{
+ GdkWindow *window = gdk_gl_context_get_window (context);
+ int width, height;
+
+ if (!gdk_gl_context_make_current (context))
+ return;
+
+ width = gdk_window_get_width (window);
+ height = gdk_window_get_height (window);
+
+ GDK_NOTE (OPENGL, g_print ("Updating GL viewport size to { %d, %d } for window %lu (context: %p)\n",
+ width, height,
+ (unsigned long) gdk_x11_window_get_xid (window),
+ context));
+
+ glViewport (0, 0, width, height);
+}
+
+static void
+maybe_wait_for_vblank (GdkDisplay *display,
+ GLXDrawable drawable)
+{
+ GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+ Display *dpy = gdk_x11_display_get_xdisplay (display);
+
+ if (display_x11->has_glx_sync_control)
+ {
+ gint64 ust, msc, sbc;
+
+ glXGetSyncValuesOML (dpy, drawable, &ust, &msc, &sbc);
+ glXWaitForMscOML (dpy, drawable,
+ 0, 2, (msc + 1) % 2,
+ &ust, &msc, &sbc);
+ }
+ else if (display_x11->has_glx_video_sync)
+ {
+ guint32 current_count;
+
+ glXGetVideoSyncSGI (¤t_count);
+ glXWaitVideoSyncSGI (2, (current_count + 1) % 2, ¤t_count);
+ }
+}
+
+void
+gdk_x11_window_invalidate_for_new_frame (GdkWindow *window,
+ cairo_region_t *update_area)
+{
+ cairo_rectangle_int_t window_rect;
+ GdkDisplay *display = gdk_window_get_display (window);
+ GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+ Display *dpy = gdk_x11_display_get_xdisplay (display);
+ GdkX11GLContext *context_x11;
+ unsigned int buffer_age;
+ gboolean invalidate_all;
+
+ /* Minimal update is ok if we're not drawing with gl */
+ if (window->gl_paint_context == NULL)
+ return;
+
+ context_x11 = GDK_X11_GL_CONTEXT (window->gl_paint_context);
+
+ buffer_age = 0;
+
+ if (display_x11->has_glx_buffer_age)
+ glXQueryDrawable(dpy, context_x11->drawable,
+ GLX_BACK_BUFFER_AGE_EXT, &buffer_age);
+
+ invalidate_all = FALSE;
+ if (buffer_age == 0 || buffer_age >= 4)
+ invalidate_all = TRUE;
+ else
+ {
+ if (buffer_age >= 2)
+ {
+ if (window->old_updated_area[0])
+ cairo_region_union (update_area, window->old_updated_area[0]);
+ else
+ invalidate_all = TRUE;
+ }
+ if (buffer_age >= 3)
+ {
+ if (window->old_updated_area[1])
+ cairo_region_union (update_area, window->old_updated_area[1]);
+ else
+ invalidate_all = TRUE;
+ }
+ }
+
+ if (invalidate_all)
+ {
+ window_rect.x = 0;
+ window_rect.y = 0;
+ window_rect.width = gdk_window_get_width (window);
+ window_rect.height = gdk_window_get_height (window);
+
+ /* If nothing else is known, repaint everything so that the back
+ buffer is fully up-to-date for the swapbuffer */
+ cairo_region_union_rectangle (update_area, &window_rect);
+ }
+
+}
+
+static void
+gdk_x11_gl_context_flush_buffer (GdkGLContext *context,
+ cairo_region_t *painted,
+ cairo_region_t *damage)
+{
+ GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context);
+ GdkWindow *window = gdk_gl_context_get_window (context);
+ GdkDisplay *display = gdk_window_get_display (window);
+ Display *dpy = gdk_x11_display_get_xdisplay (display);
+ GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+ DrawableInfo *info;
+ GLXDrawable drawable;
+
+ gdk_gl_context_make_current (context);
+
+ info = get_glx_drawable_info (window);
+
+ drawable = context_x11->drawable;
+
+ GDK_NOTE (OPENGL,
+ g_print ("Flushing GLX buffers for drawable %lu (window: %lu), frame sync: %s\n",
+ (unsigned long) drawable,
+ (unsigned long) gdk_x11_window_get_xid (window),
+ context_x11->do_frame_sync ? "yes" : "no"));
+
+ /* if we are going to wait for the vertical refresh manually
+ * we need to flush pending redraws, and we also need to wait
+ * for that to finish, otherwise we are going to tear.
+ *
+ * obviously, this condition should not be hit if we have
+ * GLX_SGI_swap_control, and we ask the driver to do the right
+ * thing.
+ */
+ if (context_x11->do_frame_sync)
+ {
+ guint32 end_frame_counter = 0;
+ gboolean has_counter = display_x11->has_glx_video_sync;
+ gboolean can_wait = display_x11->has_glx_video_sync || display_x11->has_glx_sync_control;
+
+ if (display_x11->has_glx_video_sync)
+ glXGetVideoSyncSGI (&end_frame_counter);
+
+ if (context_x11->do_frame_sync && !display_x11->has_glx_swap_interval)
+ {
+ glFinish ();
+
+ if (has_counter && can_wait)
+ {
+ guint32 last_counter = info != NULL ? info->last_frame_counter : 0;
+
+ if (last_counter == end_frame_counter)
+ maybe_wait_for_vblank (display, drawable);
+ }
+ else if (can_wait)
+ maybe_wait_for_vblank (display, drawable);
+ }
+ }
+
+ glXSwapBuffers (dpy, drawable);
+
+ if (context_x11->do_frame_sync && info != NULL && display_x11->has_glx_video_sync)
+ glXGetVideoSyncSGI (&info->last_frame_counter);
+}
+
+typedef struct {
+ Display *display;
+ GLXDrawable drawable;
+ gboolean y_inverted;
+} GdkGLXPixmap;
+
+static void
+glx_pixmap_destroy (void *data)
+{
+ GdkGLXPixmap *glx_pixmap = data;
+
+ glXDestroyPixmap (glx_pixmap->display, glx_pixmap->drawable);
+
+ g_slice_free (GdkGLXPixmap, glx_pixmap);
+}
+
+static GdkGLXPixmap *
+glx_pixmap_get (cairo_surface_t *surface)
+{
+ Display *display = cairo_xlib_surface_get_display (surface);
+ Screen *screen = cairo_xlib_surface_get_screen (surface);
+ Visual *visual = cairo_xlib_surface_get_visual (surface);;
+ GdkGLXPixmap *glx_pixmap;
+ GLXFBConfig *fbconfigs;
+ int nfbconfigs;
+ XVisualInfo *visinfo;
+ int i, value;
+ gboolean y_inverted;
+ const int pixmap_attributes[] = {
+ GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_RECTANGLE_EXT,
+ GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
+ None
+ };
+
+ y_inverted = FALSE;
+ fbconfigs = glXGetFBConfigs (display, XScreenNumberOfScreen (screen), &nfbconfigs);
+ for (i = 0; i < nfbconfigs; i++)
+ {
+ visinfo = glXGetVisualFromFBConfig (display, fbconfigs[i]);
+ if (!visinfo || visinfo->visualid != XVisualIDFromVisual (visual))
+ continue;
+
+ glXGetFBConfigAttrib (display, fbconfigs[i], GLX_DRAWABLE_TYPE, &value);
+ if (!(value & GLX_PIXMAP_BIT))
+ continue;
+
+ glXGetFBConfigAttrib (display, fbconfigs[i],
+ GLX_BIND_TO_TEXTURE_TARGETS_EXT,
+ &value);
+ if (!(value & GLX_TEXTURE_RECTANGLE_BIT_EXT))
+ continue;
+
+ glXGetFBConfigAttrib (display, fbconfigs[i],
+ GLX_BIND_TO_TEXTURE_RGBA_EXT,
+ &value);
+ if (value == FALSE)
+ {
+ glXGetFBConfigAttrib (display, fbconfigs[i],
+ GLX_BIND_TO_TEXTURE_RGB_EXT,
+ &value);
+ if (value == FALSE)
+ continue;
+ }
+
+ glXGetFBConfigAttrib (display, fbconfigs[i],
+ GLX_Y_INVERTED_EXT,
+ &value);
+ if (value == TRUE)
+ y_inverted = TRUE;
+
+ break;
+ }
+
+ if (i == nfbconfigs)
+ return NULL;
+
+ glx_pixmap = g_slice_new0 (GdkGLXPixmap);
+ glx_pixmap->y_inverted = y_inverted;
+ glx_pixmap->display = display;
+ glx_pixmap->drawable = glXCreatePixmap (display, fbconfigs[i],
+ cairo_xlib_surface_get_drawable (surface),
+ pixmap_attributes);
+
+ return glx_pixmap;
+}
+
+static gboolean
+gdk_x11_gl_context_texture_from_surface (GdkGLContext *context,
+ cairo_surface_t *surface,
+ cairo_region_t *region)
+{
+ GdkGLXPixmap *glx_pixmap;
+ double device_x_offset, device_y_offset;
+ cairo_rectangle_int_t rect;
+ int n_rects, i;
+ GdkWindow *window;
+ int window_height;
+ int window_scale;
+ unsigned int texture_id;
+ double sx, sy;
+
+ if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_XLIB)
+ return FALSE;
+
+ glx_pixmap = glx_pixmap_get (surface);
+ if (glx_pixmap == NULL)
+ return FALSE;
+
+ window = gdk_gl_context_get_window (gdk_gl_context_get_current ());
+ window_scale = gdk_window_get_scale_factor (window);
+ window_height = gdk_window_get_height (window);
+
+ sx = sy = 1;
+#ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE
+ cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy);
+#endif
+
+ cairo_surface_get_device_offset (surface,
+ &device_x_offset, &device_y_offset);
+
+ /* Ensure all the X stuff are synced before we read it back via texture-from-pixmap */
+ glXWaitX();
+
+ glGenTextures (1, &texture_id);
+ glBindTexture (GL_TEXTURE_RECTANGLE_ARB, texture_id);
+ glEnable (GL_TEXTURE_RECTANGLE_ARB);
+
+ glXBindTexImageEXT (glx_pixmap->display, glx_pixmap->drawable,
+ GLX_FRONT_LEFT_EXT, NULL);
+
+ n_rects = cairo_region_num_rectangles (region);
+ for (i = 0; i < n_rects; i++)
+ {
+ int src_x, src_y, src_height, src_width;
+
+ cairo_region_get_rectangle (region, i, &rect);
+
+ glScissor (rect.x * window_scale, (window_height - rect.y - rect.height) * window_scale,
+ rect.width * window_scale, rect.height * window_scale);
+
+ src_x = rect.x * sx + device_x_offset;
+ src_y = rect.y * sy + device_y_offset;
+ src_width = rect.width * sx;
+ src_height = rect.height * sy;
+
+#define FLIP_Y(_y) (window_height - (_y))
+
+ glBegin (GL_QUADS);
+ glTexCoord2f (src_x, src_y + src_height);
+ glVertex2f (rect.x * window_scale, FLIP_Y(rect.y + rect.height) * window_scale);
+
+ glTexCoord2f (src_x + src_width, src_y + src_height);
+ glVertex2f ((rect.x + rect.width) * window_scale, FLIP_Y(rect.y + rect.height) * window_scale);
+
+ glTexCoord2f (src_x + src_width, src_y);
+ glVertex2f ((rect.x + rect.width) * window_scale, FLIP_Y(rect.y) * window_scale);
+
+ glTexCoord2f (src_x, src_y);
+ glVertex2f (rect.x * window_scale, FLIP_Y(rect.y) * window_scale);
+ glEnd();
+ }
+
+ glXReleaseTexImageEXT (glx_pixmap->display, glx_pixmap->drawable,
+ GLX_FRONT_LEFT_EXT);
+
+ glDisable (GL_TEXTURE_RECTANGLE_ARB);
+ glDeleteTextures (1, &texture_id);
+
+ glx_pixmap_destroy(glx_pixmap);
+
+ return TRUE;
+}
+
+static void
+gdk_x11_gl_context_class_init (GdkX11GLContextClass *klass)
+{
+ GdkGLContextClass *context_class = GDK_GL_CONTEXT_CLASS (klass);
+
+ context_class->update = gdk_x11_gl_context_update;
+ context_class->flush_buffer = gdk_x11_gl_context_flush_buffer;
+ context_class->texture_from_surface = gdk_x11_gl_context_texture_from_surface;
+}
+
+static void
+gdk_x11_gl_context_init (GdkX11GLContext *self)
+{
+}
+
+gboolean
+gdk_x11_display_init_gl (GdkDisplay *display)
+{
+ GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+ GdkScreen *screen;
+ Display *dpy;
+ int error_base, event_base;
+ int screen_num;
+
+ if (display_x11->have_glx)
+ return TRUE;
+
+ dpy = gdk_x11_display_get_xdisplay (display);
+
+ if (!glXQueryExtension (dpy, &error_base, &event_base))
+ return FALSE;
+
+ screen = gdk_display_get_default_screen (display);
+ screen_num = GDK_X11_SCREEN (screen)->screen_num;
+
+ display_x11->have_glx = TRUE;
+
+ display_x11->glx_version = epoxy_glx_version (dpy, screen_num);
+ display_x11->glx_error_base = error_base;
+ display_x11->glx_event_base = event_base;
+
+ display_x11->has_glx_create_context =
+ epoxy_has_glx_extension (dpy, screen_num, "GLX_ARB_create_context_profile");
+ display_x11->has_glx_swap_interval =
+ epoxy_has_glx_extension (dpy, screen_num, "GLX_SGI_swap_control");
+ display_x11->has_glx_texture_from_pixmap =
+ epoxy_has_glx_extension (dpy, screen_num, "GLX_EXT_texture_from_pixmap");
+ display_x11->has_glx_video_sync =
+ epoxy_has_glx_extension (dpy, screen_num, "GLX_SGI_video_sync");
+ display_x11->has_glx_buffer_age =
+ epoxy_has_glx_extension (dpy, screen_num, "GLX_EXT_buffer_age");
+ display_x11->has_glx_sync_control =
+ epoxy_has_glx_extension (dpy, screen_num, "GLX_OML_sync_control");
+
+ GDK_NOTE (OPENGL,
+ g_print ("GLX version %d.%d found\n"
+ " - Vendor: %s\n"
+ " - Checked extensions:\n"
+ "\t* GLX_ARB_create_context_profile: %s\n"
+ "\t* GLX_SGI_swap_control: %s\n"
+ "\t* GLX_EXT_texture_from_pixmap: %s\n"
+ "\t* GLX_SGI_video_sync: %s\n"
+ "\t* GLX_EXT_buffer_age: %s\n"
+ "\t* GLX_OML_sync_control: %s\n",
+ display_x11->glx_version / 10,
+ display_x11->glx_version % 10,
+ glXGetClientString (dpy, GLX_VENDOR),
+ display_x11->has_glx_create_context ? "yes" : "no",
+ display_x11->has_glx_swap_interval ? "yes" : "no",
+ display_x11->has_glx_texture_from_pixmap ? "yes" : "no",
+ display_x11->has_glx_video_sync ? "yes" : "no",
+ display_x11->has_glx_buffer_age ? "yes" : "no",
+ display_x11->has_glx_sync_control ? "yes" : "no"));
+
+ return TRUE;
+}
+
+#define MAX_GLX_ATTRS 30
+
+static gboolean
+find_fbconfig_for_window (GdkWindow *window,
+ GLXFBConfig *fb_config_out,
+ XVisualInfo **visinfo_out,
+ GError **error)
+{
+ static int attrs[MAX_GLX_ATTRS];
+ GdkVisual *visual = gdk_window_get_visual (window);
+ GdkDisplay *display = gdk_window_get_display (window);
+ Display *dpy = gdk_x11_display_get_xdisplay (display);
+ GLXFBConfig *configs;
+ int n_configs, i;
+ gboolean use_rgba;
+ gboolean retval = FALSE;
+
+ i = 0;
+ attrs[i++] = GLX_DRAWABLE_TYPE;
+ attrs[i++] = GLX_WINDOW_BIT;
+
+ attrs[i++] = GLX_RENDER_TYPE;
+ attrs[i++] = GLX_RGBA_BIT;
+
+ attrs[i++] = GLX_DOUBLEBUFFER;
+ attrs[i++] = GL_TRUE;
+
+ attrs[i++] = GLX_RED_SIZE;
+ attrs[i++] = gdk_visual_get_bits_per_rgb (visual);
+ attrs[i++] = GLX_GREEN_SIZE;
+ attrs[i++] = gdk_visual_get_bits_per_rgb (visual);;
+ attrs[i++] = GLX_BLUE_SIZE;
+ attrs[i++] = gdk_visual_get_bits_per_rgb (visual);;
+
+ use_rgba = (visual == gdk_screen_get_rgba_visual (gdk_display_get_default_screen (display)));
+
+ if (use_rgba)
+ {
+ attrs[i++] = GLX_ALPHA_SIZE;
+ attrs[i++] = 1;
+ }
+ else
+ {
+ attrs[i++] = GLX_ALPHA_SIZE;
+ attrs[i++] = GLX_DONT_CARE;
+ }
+
+ attrs[i++] = None;
+
+ g_assert (i < MAX_GLX_ATTRS);
+
+ configs = glXChooseFBConfig (dpy, DefaultScreen (dpy), attrs, &n_configs);
+ if (configs == NULL || n_configs == 0)
+ {
+ g_set_error_literal (error, GDK_GL_ERROR,
+ GDK_GL_ERROR_UNSUPPORTED_FORMAT,
+ _("No available configurations for the given pixel format"));
+ return FALSE;
+ }
+
+ /* if we don't care about an alpha channel, then the first
+ * valid configuration is the one we give back
+ */
+ if (!use_rgba)
+ {
+ if (fb_config_out != NULL)
+ *fb_config_out = configs[0];
+
+ if (visinfo_out != NULL)
+ *visinfo_out = glXGetVisualFromFBConfig (dpy, configs[0]);
+
+ retval = TRUE;
+ goto out;
+ }
+
+ for (i = 0; i < n_configs; i++)
+ {
+ XVisualInfo *visinfo;
+ unsigned long mask;
+
+ visinfo = glXGetVisualFromFBConfig (dpy, configs[i]);
+ if (visinfo == NULL)
+ continue;
+
+ mask = visinfo->red_mask | visinfo->green_mask | visinfo->blue_mask;
+ if (visinfo->depth == 32 && mask != 0xffffffff)
+ {
+ if (fb_config_out != NULL)
+ *fb_config_out = configs[i];
+
+ if (visinfo_out != NULL)
+ *visinfo_out = visinfo;
+
+ retval = TRUE;
+ goto out;
+ }
+
+ XFree (visinfo);
+ }
+
+ g_set_error (error, GDK_GL_ERROR,
+ GDK_GL_ERROR_UNSUPPORTED_FORMAT,
+ _("No available configurations for the given RGBA pixel format"));
+
+out:
+ XFree (configs);
+
+ return retval;
+}
+
+static GLXContext
+create_gl3_context (GdkDisplay *display,
+ GLXFBConfig config,
+ GdkGLContext *share)
+{
+ static const int attrib_list[] = {
+ GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
+ GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
+ GLX_CONTEXT_MAJOR_VERSION_ARB, 2,
+ None,
+ };
+
+ GdkX11GLContext *context_x11 = NULL;
+
+ if (share != NULL)
+ context_x11 = GDK_X11_GL_CONTEXT (share);
+
+ return glXCreateContextAttribsARB (gdk_x11_display_get_xdisplay (display),
+ config,
+ context_x11 != NULL ? context_x11->glx_context : NULL,
+ True,
+ attrib_list);
+}
+
+static GLXContext
+create_gl_context (GdkDisplay *display,
+ GLXFBConfig config,
+ GdkGLContext *share)
+{
+ GdkX11GLContext *context_x11 = NULL;
+
+ if (share != NULL)
+ context_x11 = GDK_X11_GL_CONTEXT (share);
+
+ return glXCreateNewContext (gdk_x11_display_get_xdisplay (display),
+ config,
+ GLX_RGBA_TYPE,
+ context_x11 != NULL ? context_x11->glx_context : NULL,
+ True);
+}
+
+GdkGLContext *
+gdk_x11_window_create_gl_context (GdkWindow *window,
+ GdkGLProfile profile,
+ GdkGLContext *share,
+ GError **error)
+{
+ GdkDisplay *display = gdk_window_get_display (window);
+ GdkX11GLContext *context;
+ GdkVisual *gdk_visual;
+ GLXFBConfig config;
+ GLXContext glx_context;
+ GLXWindow drawable;
+ gboolean is_direct;
+ XVisualInfo *xvisinfo;
+ Display *dpy;
+ DrawableInfo *info;
+
+ if (!gdk_x11_display_init_gl (display))
+ {
+ g_set_error_literal (error, GDK_GL_ERROR,
+ GDK_GL_ERROR_NOT_AVAILABLE,
+ _("No GL implementation is available"));
+ return NULL;
+ }
+
+ if (profile == GDK_GL_PROFILE_3_2_CORE &&
+ !GDK_X11_DISPLAY (display)->has_glx_create_context)
+ {
+ g_set_error_literal (error, GDK_GL_ERROR,
+ GDK_GL_ERROR_UNSUPPORTED_PROFILE,
+ _("The GLX_ARB_create_context_profile extension "
+ "needed to create 3.2 core profiles is not "
+ "available"));
+ return NULL;
+ }
+
+ if (!find_fbconfig_for_window (window, &config, &xvisinfo, error))
+ return NULL;
+
+ dpy = gdk_x11_display_get_xdisplay (display);
+
+ /* we check for the GLX_ARB_create_context_profile extension
+ * while validating the PixelFormat.
+ */
+ if (profile == GDK_GL_PROFILE_3_2_CORE)
+ glx_context = create_gl3_context (display, config, share);
+ else
+ {
+ /* GDK_GL_PROFILE_DEFAULT is currently
+ * equivalent to the LEGACY profile
+ */
+ glx_context = create_gl_context (display, config, share);
+ }
+
+ if (glx_context == NULL)
+ {
+ g_set_error_literal (error, GDK_GL_ERROR,
+ GDK_GL_ERROR_NOT_AVAILABLE,
+ _("Unable to create a GL context"));
+ return NULL;
+ }
+
+ is_direct = glXIsDirect (dpy, glx_context);
+
+ gdk_x11_display_error_trap_push (display);
+
+ if (GDK_X11_DISPLAY (display)->glx_version >= 13)
+ {
+ info = get_glx_drawable_info (window->impl_window);
+
+ if (info == NULL)
+ {
+ info = g_slice_new (DrawableInfo);
+ info->window = window->impl_window;
+ info->display = display;
+ info->drawable = glXCreateWindow (dpy,
+ config,
+ gdk_x11_window_get_xid (window->impl_window),
+ NULL);
+ info->last_frame_counter = 0;
+
+ set_glx_drawable_info (window->impl_window, info);
+ }
+
+ drawable = info->drawable;
+ }
+ else
+ {
+ drawable = gdk_x11_window_get_xid (window);
+ }
+
+ gdk_visual = gdk_x11_screen_lookup_visual (gdk_display_get_default_screen (display),
+ xvisinfo->visualid);
+
+ XFree (xvisinfo);
+
+ if (gdk_x11_display_error_trap_pop (display))
+ {
+ g_set_error_literal (error, GDK_GL_ERROR,
+ GDK_GL_ERROR_NOT_AVAILABLE,
+ _("Unable to create a GL context"));
+
+ glXDestroyContext (dpy, glx_context);
+
+ return NULL;
+ }
+
+ GDK_NOTE (OPENGL,
+ g_print ("Created GLX context[%p], %s\n",
+ glx_context,
+ is_direct ? "direct" : "indirect"));
+
+ context = g_object_new (GDK_X11_TYPE_GL_CONTEXT,
+ "window", window,
+ "visual", gdk_visual,
+ NULL);
+
+ context->glx_config = config;
+ context->glx_context = glx_context;
+ context->drawable = drawable;
+ context->is_direct = is_direct;
+
+ return GDK_GL_CONTEXT (context);
+}
+
+void
+gdk_x11_display_destroy_gl_context (GdkDisplay *display,
+ GdkGLContext *context)
+{
+ GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context);
+ Display *dpy = gdk_x11_display_get_xdisplay (display);
+
+ if (context_x11->glx_context != NULL)
+ {
+ if (glXGetCurrentContext () == context_x11->glx_context)
+ glXMakeContextCurrent (dpy, None, None, NULL);
+
+ GDK_NOTE (OPENGL, g_print ("Destroying GLX context\n"));
+ glXDestroyContext (dpy, context_x11->glx_context);
+ context_x11->glx_context = NULL;
+ }
+}
+
+gboolean
+gdk_x11_display_make_gl_context_current (GdkDisplay *display,
+ GdkGLContext *context)
+{
+ GdkX11GLContext *context_x11;
+ Display *dpy = gdk_x11_display_get_xdisplay (display);
+ GdkWindow *window;
+ GdkScreen *screen;
+ gboolean do_frame_sync = FALSE;
+
+ if (context == NULL)
+ {
+ glXMakeContextCurrent (dpy, None, None, NULL);
+ return TRUE;
+ }
+
+ context_x11 = GDK_X11_GL_CONTEXT (context);
+
+ if (context_x11->glx_context == NULL)
+ return FALSE;
+
+ window = gdk_gl_context_get_window (context);
+
+ // If the WM is compositing there is no particular need to delay
+ // the swap when drawing on the offscreen, rendering to the screen
+ // happens later anyway, and its up to the compositor to sync that
+ // to the vblank.
+ screen = gdk_window_get_screen (window);
+ do_frame_sync = ! gdk_screen_is_composited (screen);
+
+ context_x11->do_frame_sync = do_frame_sync;
+
+ GDK_NOTE (OPENGL,
+ g_print ("Making GLX context current to drawable %lu\n",
+ (unsigned long) context_x11->drawable));
+
+ gdk_x11_display_error_trap_push (display);
+
+ glXMakeContextCurrent (dpy, context_x11->drawable, context_x11->drawable,
+ context_x11->glx_context);
+
+ if (GDK_X11_DISPLAY (display)->has_glx_swap_interval)
+ {
+ if (context_x11->do_frame_sync)
+ glXSwapIntervalSGI (1);
+ else
+ glXSwapIntervalSGI (0);
+ }
+
+ /* TODO: Is this needed? */
+ XSync (dpy, False);
+
+ if (gdk_x11_display_error_trap_pop (display))
+ {
+ g_critical ("X Error received while calling glXMakeContextCurrent()");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gdk_x11_display_get_glx_version:
+ * @display: a #GdkDisplay
+ * @major: (out): return location for the GLX major version
+ * @minor: (out): return location for the GLX minor version
+ *
+ * Retrieves the version of the GLX implementation.
+ *
+ * Returns: %TRUE if GLX is available
+ *
+ * Since: 3.14
+ */
+gboolean
+gdk_x11_display_get_glx_version (GdkDisplay *display,
+ int *major,
+ int *minor)
+{
+ g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+
+ if (!GDK_IS_X11_DISPLAY (display))
+ return FALSE;
+
+ if (!gdk_x11_display_init_gl (display))
+ return FALSE;
+
+ if (major != NULL)
+ *major = GDK_X11_DISPLAY (display)->glx_version / 10;
+ if (minor != NULL)
+ *minor = GDK_X11_DISPLAY (display)->glx_version % 10;
+
+ return TRUE;
+}
--- /dev/null
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkglcontext-x11.h: Private X11 specific OpenGL wrappers
+ *
+ * Copyright © 2014 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_X11_GL_CONTEXT__
+#define __GDK_X11_GL_CONTEXT__
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+#include <epoxy/gl.h>
+#include <epoxy/glx.h>
+
+#include "gdkglcontextprivate.h"
+#include "gdkdisplayprivate.h"
+#include "gdkvisual.h"
+#include "gdkwindow.h"
+#include "gdkinternals.h"
+#include "gdkmain.h"
+
+G_BEGIN_DECLS
+
+struct _GdkX11GLContext
+{
+ GdkGLContext parent_instance;
+
+ GLXContext glx_context;
+ GLXFBConfig glx_config;
+ GLXDrawable drawable;
+
+ guint is_direct : 1;
+ guint do_frame_sync : 1;
+
+};
+
+struct _GdkX11GLContextClass
+{
+ GdkGLContextClass parent_class;
+};
+
+gboolean gdk_x11_display_init_gl (GdkDisplay *display);
+GdkGLContext * gdk_x11_window_create_gl_context (GdkWindow *window,
+ GdkGLProfile profile,
+ GdkGLContext *share,
+ GError **error);
+void gdk_x11_window_invalidate_for_new_frame (GdkWindow *window,
+ cairo_region_t *update_area);
+void gdk_x11_display_destroy_gl_context (GdkDisplay *display,
+ GdkGLContext *context);
+gboolean gdk_x11_display_make_gl_context_current (GdkDisplay *display,
+ GdkGLContext *context);
+
+G_END_DECLS
+
+#endif /* __GDK_X11_GL_CONTEXT__ */
#include "gdkasync.h"
#include "gdkeventsource.h"
#include "gdkdisplay-x11.h"
+#include "gdkglcontext-x11.h"
#include "gdkprivate-x11.h"
#include <stdlib.h>
impl_class->set_opaque_region = gdk_x11_window_set_opaque_region;
impl_class->set_shadow_width = gdk_x11_window_set_shadow_width;
impl_class->show_window_menu = gdk_x11_window_show_window_menu;
+ impl_class->create_gl_context = gdk_x11_window_create_gl_context;
+ impl_class->invalidate_for_new_frame = gdk_x11_window_invalidate_for_new_frame;
}
#include <gdk/x11/gdkx11display.h>
#include <gdk/x11/gdkx11displaymanager.h>
#include <gdk/x11/gdkx11dnd.h>
+#include <gdk/x11/gdkx11glcontext.h>
#include <gdk/x11/gdkx11keys.h>
#include <gdk/x11/gdkx11property.h>
#include <gdk/x11/gdkx11screen.h>
--- /dev/null
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkglcontext-x11.c: X11 specific OpenGL wrappers
+ *
+ * Copyright © 2014 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_X11_GL_CONTEXT_H__
+#define __GDK_X11_GL_CONTEXT_H__
+
+#if !defined (__GDKX_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdkx.h> can be included directly."
+#endif
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define GDK_X11_TYPE_GL_CONTEXT (gdk_x11_gl_context_get_type ())
+#define GDK_X11_GL_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_X11_TYPE_GL_CONTEXT, GdkX11GLContext))
+#define GDK_X11_IS_GL_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_X11_TYPE_GL_CONTEXT))
+
+typedef struct _GdkX11GLContext GdkX11GLContext;
+typedef struct _GdkX11GLContextClass GdkX11GLContextClass;
+
+GDK_AVAILABLE_IN_3_16
+GType gdk_x11_gl_context_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_16
+gboolean gdk_x11_display_get_glx_version (GdkDisplay *display,
+ int *major,
+ int *minor);
+
+G_END_DECLS
+
+#endif /* __GDK_X11_GL_CONTEXT_H__ */